home *** CD-ROM | disk | FTP | other *** search
- /*========================================================================
- *
- * IDENTIFICATION:
- * $Header: /private/postgres/src/rules/prs2/RCS/prs2view.c,v 1.12 1991/11/18 22:21:22 mer Exp $
- *
- * DESCRIPTION:
- * This file contains (among other things!) the code for 'view-like' rules,
- * i.e. rules that return many tuples at a time like:
- * ON retrieve to <relation>
- * DO [ instead ] retrieve ......
- *
- * (if we specify a 'instead' then this is a virtual view,
- * otherwise it might be partially materialized)
- *
- *
- *========================================================================
- */
-
- #include <stdio.h>
- #include "utils/log.h"
- #include "executor/execdefs.h"
- #include "rules/prs2locks.h"
- #include "rules/prs2.h"
- #include "rules/prs2stub.h"
- #include "nodes/execnodes.h"
- #include "parser/parse.h" /* for RETRIEVE, APPEND etc. */
- #include "utils/palloc.h"
-
- extern EState CreateExecutorState();
- extern LispValue ExecMain();
-
- /*-----------------------------------------------------------------------
- * prs2MakeRelationRuleInfo(relation)
- *
- * This initializes some information about the rules affecting a
- * relation.
- *
- * There are 2 places where this routine can be called:
- *
- * A) by 'ExecInitSeqScan' and 'ExecInitIndexScan'
- * when the 'ScanState' of a 'Scan' node is intialized.
- * In this case `operation' must be RETRIEVE
- *
- * B) by InitPlan when we have an update plan (i.e. a delete, append
- * or a replace command). In this case the 'operation' must be
- * APPEND, DELETE or REPLaCE.
- *
- * This scan state contains among other things some information
- * needed by the rule manager. See commnets in the definition of the
- * 'RelationRuleInfo' struct for more details....
- *
- */
- RelationRuleInfo
- prs2MakeRelationRuleInfo(relation, operation)
- Relation relation;
- int operation;
- {
- RuleLock locks;
- Name relationName;
- int i, j, nlocks;
- Prs2OneLock oneLock;
- Prs2LockType lockType;
- ObjectId ruleId;
- Prs2PlanNumber planNo;
- long size;
- int nrules;
- Prs2RuleList ruleList, ruleListItem;
- Prs2Stub stubs;
- RelationRuleInfo relationRuleInfo;
-
-
- /*
- * find the locks of the relation...
- */
- relationName = RelationGetRelationName(relation);
- locks = prs2GetLocksFromRelation(relationName);
-
- /*
- * create a linked list of all the rule ids that hold a
- * 'LockTypeRetrieveRelation'
- */
- ruleList = NULL;
- if (operation == RETRIEVE) {
- nlocks = prs2GetNumberOfLocks(locks);
- for (i=0; i<nlocks ; i++) {
- oneLock = prs2GetOneLockFromLocks(locks, i);
- lockType = prs2OneLockGetLockType(oneLock);
- ruleId = prs2OneLockGetRuleId(oneLock);
- planNo = prs2OneLockGetPlanNumber(oneLock);
- if (lockType==LockTypeRetrieveRelation) {
- ruleListItem = (Prs2RuleList) palloc(sizeof(Prs2RuleListItem));
- if (ruleListItem == NULL) {
- elog(WARN,"prs2MakeRuleList: Out of memory");
- }
- ruleListItem->type = PRS2_RULELIST_RULEID;
- ruleListItem->data.ruleId.ruleId = ruleId;
- ruleListItem->data.ruleId.planNumber = planNo;
- ruleListItem->next = ruleList;
- ruleList = ruleListItem;
- }
- }
- }
-
- /*
- * now find the relation level rule stubs.
- */
- stubs = prs2GetRelationStubs(RelationGetRelationId(relation));
-
- /*
- * Create and return a 'RelationRuleInfo' structure..
- */
- size = sizeof(RelationRuleInfoData);
- relationRuleInfo = (RelationRuleInfo) palloc(size);
- if (relationRuleInfo == NULL) {
- elog(WARN,
- "prs2MakeRelationRuleInfo: Out of memory! (%ld bytes requested)",
- size);
- }
-
- relationRuleInfo->ruleList = ruleList;
- relationRuleInfo->insteadViewRuleFound = false;
- relationRuleInfo->relationLocks = locks;
- relationRuleInfo->relationStubs = stubs;
- relationRuleInfo->relationStubsHaveChanged = false;
-
- /*
- * if this is the "pg_class" then ignore all tuple level locks
- * found in tuples, because these locks are not "real" tuple level
- * locks of rules defined in "pg_class", but they are relation level
- * locks defined on various other relations.
- */
- if (!strcmp(relationName, Name_pg_relation))
- relationRuleInfo->ignoreTupleLocks = true;
- else
- relationRuleInfo->ignoreTupleLocks = false;
-
- return(relationRuleInfo);
-
- }
-
- /*-----------------------------------------------------------------------
- *
- * prs2GetOneTupleFromViewRules
- */
- HeapTuple
- prs2GetOneTupleFromViewRules(relationRuleInfo, prs2EStateInfo, relation,
- explainRel)
- RelationRuleInfo relationRuleInfo;
- Prs2EStateInfo prs2EStateInfo;
- Relation relation;
- Relation explainRel;
- {
-
- long size;
- Prs2RuleList tempRuleList, tempRuleListItem;
- ParamListInfo paramList;
- LispValue plan, ruleInfo, planQual, planActions, onePlan;
- LispValue queryDesc;
- EState executorState;
- LispValue res;
- HeapTuple tuple;
-
-
- /*
- * Process the first record of 'ruleList',
- * until either the lsit becomes empty, or
- * a new tuple is created....
- */
- while(relationRuleInfo->ruleList != NULL) {
- switch (relationRuleInfo->ruleList->type) {
- case PRS2_RULELIST_RULEID:
- /*
- * Fetch the plan from Prs2PlansRelation.
- */
- plan = prs2GetRulePlanFromCatalog(
- relationRuleInfo->ruleList->data.ruleId.ruleId,
- relationRuleInfo->ruleList->data.ruleId.planNumber,
- ¶mList);
- /*
- * It is possible that plan = nil (an obsolete rule?)
- * In this case ignore the rule.
- */
- if (null(plan)) {
- break;
- }
- /*
- * NOTE: the `paramList' must be empty!
- * because we do not allow 'NEW' & 'OLD' in 'view-like' rules.
- * Note though, that the rule qualification might not
- * be empty. E.g. it might be something like:
- * where user() == "Spyros the Great"
- */
- if (paramList != NULL && paramList[0].kind != PARAM_INVALID) {
- elog(WARN, "View Rules must not have parameters!\n");
- }
- ruleInfo = prs2GetRuleInfoFromActionPlan(plan);
- planQual = prs2GetQualFromActionPlan(plan);
- planActions = prs2GetActionsFromActionPlan(plan);
- /*
- * test the rule qualification and if true, then
- * for every plan specified in the action part of the
- * rule, create a Prs2RuleListItem and insert it
- * in a linked list.
- */
- if (prs2CheckQual(planQual, paramList, prs2EStateInfo)) {
- tempRuleList = relationRuleInfo->ruleList->next;
- foreach(onePlan, planActions) {
- size = sizeof(Prs2RuleListItem);
- tempRuleListItem = (Prs2RuleList) palloc(size);
- if (tempRuleListItem == NULL) {
- elog(WARN,
- "prs2GetOneTupleFromViewRules: Out of memory");
- }
- tempRuleListItem->type = PRS2_RULELIST_PLAN;
- tempRuleListItem->data.rulePlan.plan = CAR(onePlan);
- tempRuleListItem->next = tempRuleList;
- tempRuleList = tempRuleListItem;
- }
- /*
- * no more action plans for this rule.
- * replace the Prs2RuleListItem of theis rule with
- * the chain of Prs2RuleListItem(s) we have just
- * created.
- */
- tempRuleListItem = relationRuleInfo->ruleList;
- relationRuleInfo->ruleList = tempRuleList;
- pfree((Pointer)tempRuleListItem);
- /*
- * check for 'instead' rules...
- */
- if (prs2IsRuleInsteadFromRuleInfo(ruleInfo)) {
- relationRuleInfo->insteadViewRuleFound = true;
- }
- }
- break;
- case PRS2_RULELIST_PLAN:
- /*
- * this is a rule plan. COnstruct the query descriptor,
- * the EState, and call the executor to perform the
- * EXEC_START operation...
- */
- queryDesc = prs2MakeQueryDescriptorFromPlan(
- relationRuleInfo->ruleList->data.rulePlan.plan);
- executorState = CreateExecutorState();
- set_es_param_list_info(executorState, paramList);
- set_es_prs2_info(executorState, prs2EStateInfo);
- ExecMain(queryDesc, executorState,
- lispCons(lispInteger(EXEC_START), LispNil));
- size = sizeof(Prs2RuleListItem);
- tempRuleList = (Prs2RuleList) palloc(size);
- if (tempRuleList == NULL) {
- elog(WARN,
- "prs2GetOneTupleFromViewRules: Out of memory");
- }
- tempRuleList->type = PRS2_RULELIST_QUERYDESC;
- tempRuleList->data.queryDesc.queryDesc = queryDesc;
- /*
- * NOTE: the following typecast is due to a stupid
- * circular dependency in the definitions of `EState' and
- * `Prs2RuleList' which obliged me to declare
- * `Prs2RuleList->data.queryDesc.estate' not as an `EState'
- * (which is the correct) but as a (struct *). Argh!
- */
- tempRuleList->data.queryDesc.estate = (Pointer) executorState;
- tempRuleList->next = relationRuleInfo->ruleList->next;
- tempRuleListItem = relationRuleInfo->ruleList;
- relationRuleInfo->ruleList = tempRuleList;
- pfree((Pointer)tempRuleListItem);
- break;
- case PRS2_RULELIST_QUERYDESC:
- /*
- * At last! a query desciptor (and an EState!)
- * properly initialized...
- * Call exec main to retrieve a tuple...
- */
- res = ExecMain(
- relationRuleInfo->ruleList->data.queryDesc.queryDesc,
- relationRuleInfo->ruleList->data.queryDesc.estate,
- lispCons(lispInteger(EXEC_RETONE), LispNil));
- tuple = (HeapTuple) ExecFetchTuple((TupleTableSlot)res);
- if (tuple != NULL) {
- /*
- * Yeahhh! the executor returned a tuple!
- *
- * Make a COPY of this tuple
- * we need to make a copy for 2 reasons:
- * a) the (low level) executor might release it when
- * called with EXEC_END
- * b) the top level executor might want to pfree it
- * and pfree seems to complain (I guess something
- * to do with the fact that this palloc happenned
- * in the lower executor's context and not in
- * the current one...
- */
- tuple = palloctup(tuple, InvalidBuffer, relation);
- return(tuple);
- }
- /*
- * No more tuples! Remove this record from the
- * relationRuleInfo->ruleList.
- * But before doing that, be nice and gracefully
- * let the executor release whatever it has to...
- */
- ExecMain(relationRuleInfo->ruleList->data.queryDesc.queryDesc,
- relationRuleInfo->ruleList->data.queryDesc.estate,
- lispCons(lispInteger(EXEC_END), LispNil));
- tempRuleList = relationRuleInfo->ruleList;
- relationRuleInfo->ruleList = relationRuleInfo->ruleList->next;
- pfree((Pointer)tempRuleList);
- break;
- } /* switch */
- } /* while */
-
- /*
- * No (more) rules found, return NULL
- */
- return(NULL);
- }
-